知识点名称:方向传感器
编号: K12-7
前驱知识点编号:K12-1,K12-2
作者:
讲义内容:
Android获取手机旋转的方向和角度是通过加速度传感器和地磁传感器共同计算出来的,这两个传感器使用的坐标轴系统仍然是图12.1的坐标轴系统:
其中正的旋转方向代表了沿着坐标轴逆时钟方向旋转。
使用这两个传感器需要获取这两个传感器的实例,并注册监听器,如下所示:
Sensor magneticSensor = sensorManager
.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
Sensor accelerometerSensor = sensorManager
.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
sensorManager.registerListener(listener, magneticSensor,
SensorManager.SENSOR_DELAY_GAME);
sensorManager.registerListener(listener, accelerometerSensor,
SensorManager.SENSOR_DELAY_GAME);
由于检测方向数据要求精度高,这里我们需要把传感器采样频率提高,使用的参数是SENSOR_DELAY_GAME。
接下来就可以在onSensorChanged()方法中获取到SensorEvent对象中的原始数据,也就是values数组,分别记录了加速度传感器和地磁传感器输出的值。然后再将这两个数组传入到SensorManager的getRotationMatrix()方法中得到一个包含旋转矩阵的R数组,如下所示:SensorManager.getRotationMatrix(R, null, accelerometerValues,
magneticValues);
Values是一个长度为3的float数组,手机在各个方向上的旋转数据都会被保存到这个数组当中,其中values[0]记录了图12.1绕Z轴旋转弧度,values[1]记录了图12.1绕X轴的旋转弧度,values[2]记录了手机围绕Y轴的旋转弧度。
最后还需要将弧度转换成角度,方法如下所示,这里把围绕Z轴旋转的弧度转换成了角度:
Math.toDegrees(values[0]);
接下来我们利用方向传感器实现一个指南针的应用,新建一个CompassTest的工程,导入两张图片compass.png和arrow.png,分别用于作为指南针的背景和指针,如图12.3所示:
图12.3 图片素材
然后修改活动的布局activity_main.xml中的代码,如下所示:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<ImageView
android:id="@+id/compass_img"
android:layout_width="250dp"
android:layout_height="250dp"
android:layout_centerInParent="true"
android:src="@drawable/compass" />
<ImageView
android:id="@+id/arrow_img"
android:layout_width="60dp"
android:layout_height="110dp"
android:layout_centerInParent="true"
android:src="@drawable/arrow" />
</RelativeLayout>
这里使用了两个ImageView控件,都居中显示,显示的图片分别是指南针的背景和指针。
接下来修改MainActivity中的代码,如下所示:
public class MainActivity extends Activity {
private SensorManager sensorManager;
private ImageView compassImg;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
compassImg = (ImageView) findViewById(R.id.compass_img);
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
Sensor magneticSensor = sensorManager
.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
Sensor accelerometerSensor = sensorManager
.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
sensorManager.registerListener(listener, magneticSensor,
SensorManager.SENSOR_DELAY_GAME);
sensorManager.registerListener(listener, accelerometerSensor,
SensorManager.SENSOR_DELAY_GAME);
}
@Override
protected void onDestroy() {
super.onDestroy();
if (sensorManager != null) {
sensorManager.unregisterListener(listener);
}
}
private SensorEventListener listener = new SensorEventListener() {
float[] accelerometerValues = new float[3];
float[] magneticValues = new float[3];
private float lastRotateDegree;
@Override
public void onSensorChanged(SensorEvent event) {
// 判断当前是加速度传感器还是地磁传感器
if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
// 注意赋值时要调用clone()方法
accelerometerValues = event.values.clone();
} else if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) {
// 注意赋值时要调用clone()方法
magneticValues = event.values.clone();
}
float[] values = new float[3];
float[] R = new float[9];
SensorManager.getRotationMatrix(R, null, accelerometerValues,
magneticValues);
SensorManager.getOrientation(R, values);
float rotateDegree = -(float) Math.toDegrees(values[0]);
// 将旋转角度取反,用于旋转指南针背景图片
if (Math.abs(rotateDegree - lastRotateDegree) > 1) {
// 第1个参数原来的角度
// 第2个参数旋转的角度
// 第3,4个参数确定屏幕平面X轴上旋转的轴心,这里设置的图片自己的中心
// 第4,5个参数确定屏幕平面Y轴上旋转的轴心,这里设置的图片自己的中心
RotateAnimation animation = new RotateAnimation(
lastRotateDegree, rotateDegree,
Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f);
animation.setFillAfter(true);
compassImg.startAnimation(animation);
lastRotateDegree = rotateDegree;
}
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
};
}
当用户旋转手机时,指南针的背景图也会跟着一起转动,最后程序运行的结果如下图所示: